package cn.head;

import javassist.ClassPool;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;


public class MyJvmMemoryOutErrors {
	/**
	 * Metaspace 静态成员
	 */
	static ClassPool cp = ClassPool.getDefault();

	/**
	 * -Xmx30m -Xms20m -Xmn6m -XX:SurvivorRatio=8 -XX:+PrintGCDetails
	 *
	 * @param args
	 */

	public static void main(String[] args) {

		// 测试堆内存溢出new30m
		byte[] b0 = new byte[30 * 1024 * 1024];
		// 模拟分配对象消耗内存
		byte[] b1 = new byte[2 * 1024 * 1024];
		byte[] b2 = new byte[2 * 1024 * 1024];
		byte[] b3 = new byte[2 * 1024 * 1024];
		byte[] b4 = new byte[4 * 1024 * 1024];
		System.out.println("max mem=" + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "M");
		System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024 / 1024 + "M");
//		注：Java会尽可能将total mem的值维持在最小堆。
		System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024 / 1024 + "M");
		System.gc();
		// 测试结果
		/**
		 * [GC (Allocation Failure) [PSYoungGen: 5034K->488K(5632K)]
		 * 5034K->4672K(19968K), 0.0322586 secs] [Times: user=0.03 sys=0.00, real=0.04
		 * secs] max mem=29M free mem=8M total mem=19M [GC (System.gc()) [PSYoungGen:
		 * 2636K->488K(5632K)] 10916K->10856K(19968K), 0.0010739 secs] [Times: user=0.00
		 * sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen:
		 * 488K->0K(5632K)] [ParOldGen: 10368K->10789K(14336K)] 10856K->10789K(19968K),
		 * [Metaspace: 2700K->2700K(1056768K)], 0.0109474 secs] [Times: user=0.00
		 * sys=0.00, real=0.01 secs] Heap PSYoungGen total 5632K, used 102K
		 * [0x00000000ffa00000, 0x0000000100000000, 0x0000000100000000) eden space
		 * 5120K, 2% used [0x00000000ffa00000,0x00000000ffa19b10,0x00000000fff00000)
		 * from space 512K, 0% used
		 * [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) to space 512K, 0%
		 * used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) ParOldGen
		 * total 14336K, used 10789K [0x00000000fe200000, 0x00000000ff000000,
		 * 0x00000000ffa00000) object space 14336K, 75% used
		 * [0x00000000fe200000,0x00000000fec89580,0x00000000ff000000) Metaspace used
		 * 2706K, capacity 4486K, committed 4864K, reserved 1056768K class space used
		 * 300K, capacity 386K, committed 512K, reserved 1048576K
		 *
		 */

	}


	/**
	 * -Xmx12m -XX:SurvivorRatio=8 -XX:+PrintGCDetails
	 * [GC (Allocation Failure) [PSYoungGen: 3072K->488K(3584K)] 3072K->850K(11776K), 0.0126607 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] hello
	 * [GC (Allocation Failure) [PSYoungGen: 2734K->504K(3584K)] 3096K->1240K(11776K), 0.0009245 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
	 * [GC (Allocation Failure) [PSYoungGen: 504K->488K(3584K)] 1240K->1264K(11776K), 0.0010857 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
	 * [Full GC (Allocation Failure) [PSYoungGen: 488K->0K(3584K)] [ParOldGen: 776K->1130K(5632K)] 1264K->1130K(9216K), [Metaspace: 4754K->4754K(1056768K)], 0.0170101 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
	 * [GC (Allocation Failure) [PSYoungGen: 0K->0K(3584K)] 1130K->1130K(11776K), 0.0038380 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
	 * [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(3584K)] [ParOldGen: 1130K->1085K(7168K)] 1130K->1085K(10752K), [Metaspace: 4754K->4754K(1056768K)], 0.0077903 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
	 * <p>
	 * java.lang.OutOfMemoryError: Java heap space
	 * 当设置为-Xmx13正常
	 */
	@Test
	public void simpleOutHeadSpace() {

		System.out.println("hello");
		int[] i = new int[2 * 1024 * 1024];

	}

	/**
	 * 内存泄漏示例
	 */
	@Test
	public void objectOutHeadSpace() {
		Map m = new HashMap();
		while (true) {
			for (int i = 0; i < 10; i++) {
				if (!m.containsKey(new Key(i))) {
					m.put(new Key(i), "Number:" + i);
				}
			}
			System.out.println("m.size()=" + m.size());
		}
	}

	/**
	 * GC overhead limit    -Xmx500m -XX:+PrintGCDetails
	 * java.lang.OutOfMemoryError: GC overhead limit exceeded 错误只在连续多次 GC 都只回收了不到2%的
	 * 更改垃圾收集器以后-XX:+UseParallelGC会得到不同的结果
	 */
	@Test
	public void gccOverHeadlimt() {
		Map map = System.getProperties();
		Random r = new Random();
		while (true) {
			map.put(r.nextInt(), "value");
		}
	}

	/**
	 * jdk7 PermGen -XX:+PrintGCDetails -Xmx200M -XX:MaxPermSize=16M
	 */
	@Test
	public void permGen() throws Exception {
		for (int i = 0; i < 100_000_000; i++) {
			generate("eu.plumbr.demo.Generated" + i);
		}
	}

	private static Class generate(String name) throws Exception {
		ClassPool pool = ClassPool.getDefault();
		return pool.makeClass(name).toClass();
	}

	/**
	 * jdk8 Metaspace MemoryOutError    -XX:+PrintGCDetails -Xmx200M -XX:MaxMetaspaceSize=16M
	 * Metaspace 的最大值设置为 512MB, 如果没有用完, 就不会抛出 OutOfMemoryError。如果不设置大小将影响物理内存swap响应慢
	 * Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
	 *
	 * @throws Exception
	 */
	@Test
	public void metaSpaceOut() throws Exception {
		for (int i = 0; ; i++) {
			Class c = cp.makeClass("eu.plumbr.demo.Generated" + i).toClass();
		}

	}

	/**
	 * java.lang.OutOfMemoryError: unable to create new native thread
	 * 更多的情况, 触发创建 native 线程时的OutOfMemoryError, 表明编程存在BUG.
	 */
	@Test
	public void createNativeThreaFailure() {
		while (true) {
			new Thread(() -> {
				try {
					Thread.sleep(10000000);
				} catch (InterruptedException e) {}
			}).start();
		}
	}

	/**
	 * Requested array size exceeds VM limit
	 *
	 * 请注意, 在后两次迭代抛出 java.lang.OutOfMemoryError: Requested array size exceeds VM limit
	 * 错误之前, 先抛出了2次 java.lang.OutOfMemoryError: Java heap space 错误。 这是因为 2^31-1 个 int 数占用的内存超过了JVM默认的8GB堆内存
	 * 数组太大, 最终长度超过平台限制值, 但小于 Integer.MAX_INT (分批加载)
	 * 为了测试系统限制, 故意分配长度大于 2^31-1 的数组。
	 */
	@Test
	public void arrayOutHead (){
		for (int i = 3; i >= 0; i--) {
			try {
				int[] arr = new int[Integer.MAX_VALUE-i];
				System.out.format("Successfully initialized an array with %,d elements.\n", Integer.MAX_VALUE-i);
			} catch (Throwable t) {
				t.printStackTrace();
			}
		}
	}
	@Test
	public  void OOM(){
		java.util.List<int[]> l = new java.util.ArrayList();
		for (int i = 10000; i < 100000; i++) {
			try {
				l.add(new int[100_000_000]);
			} catch (Throwable t) {
				t.printStackTrace();
			}
		}
	}

	/**
	 * 测试对象
	 */
	class Key {
		byte[] b = new byte[1 * 1024 * 1024];
		private Integer id;

		Key(Integer id) {
			this.id = id;
		}

		@Override
		public int hashCode() {
			return id.hashCode();
		}

		/**
		 * 重写后就不存在栈溢出
		 */
		@Override
		public boolean equals(Object o) {
			boolean response = false;
			if (o instanceof Key) {
				response = (((Key) o).id).equals(this.id);
			}
			return response;
		}
	}

}
